到上一篇文章為止,TDD中所需具備的基本測試知識,已經告一段落。
接下來要練習的,是重構的手法。
接下來幾篇文章,會跟各位讀者朋友介紹:
這一篇文章則會先介紹,要如何找到程式碼中需要重構的地方。
上一篇文章:[Day 8]Integration Testing & Web UI Testing
本系列文章專區
@現況
我們所面臨的系統狀況,通常也就是Legacy Code(提到Legacy Code,就要順便介紹一本好書:Working Effectively with Legacy Code),就像下圖一樣:
圖片來源:圖片來源:http://www.chancedia.com/?p=41470
就像廣告說的一樣:「每個Dev都喜歡乾淨的code,但是又喜歡把code弄髒。」
@重構的目的
我們希望可以把雜亂無章的code,乾淨整齊的放在它們所屬的位置上。
圖片來源:http://jung9572002.pixnet.net/blog/post/1733351-%E6%94%B6%E7%B4%8D%E9%81%94%E4%BA%BA
@重構的時機與目標
基本上最適合重構的時機有三類:
圖片來源:http://awards.gettyimages.com/awards.cfm?selCategory=all&display=photographer&workID=67&photographerID=7&photoID=70&sp_sortID=1
圖片來源:http://www.lykasal.com/2012/10/cats-that-pester-for-food-could-be.html
圖片來源:http://www.thetorontopost.net/2012/09/smell-test-total-fail-for-rob-ford-in.html
簡單的說,就是要修改程式的時候,或是程式很髒的時候,適合重構。
但請記住:「一次只做一件事」
@如何找出Bad Smell
這邊的範例,我建立了一個不同物流商會計算出不同運費的網站。
先以SourceMonitor為例,來找出系統中複雜度太高的function,並將它當做我們重構的目標。(SourceMonitor的介紹,有興趣的朋友可以看之前這篇文章:[Tool]SourceMonitor - 程式碼掃瞄)
掃描後,按照Max Complexity排序,可以看到Prodcut_v0.aspx.cs,最大複雜度14,最大深度5。如下圖所示:
再點開詳細資訊後,可以看到 btnCalculate_Click這個方法,就是造成最大複雜度與最大深度的原因。如下圖所示:
[註]也可以使用VS2012/VS2010的程式碼度量,來找到複雜度過高的程式
接著,來看一下這個function的程式碼,如下所示:
protected void btnCalculate_Click(object sender, EventArgs e)
{
if (this.IsValid)
{
if (this.drpCompany.SelectedValue == "1")
{
this.lblCompany.Text = "黑貓";
var weight = Convert.ToDouble(this.txtProductWeight.Text);
if (weight > 20)
{
this.lblCharge.Text = "500";
}
else
{
var fee = 100 + weight * 10;
this.lblCharge.Text = fee.ToString();
}
}
else if (this.drpCompany.SelectedValue == "2")
{
this.lblCompany.Text = "新竹貨運";
var length = Convert.ToDouble(this.txtProductLength.Text);
var width = Convert.ToDouble(this.txtProductWidth.Text);
var height = Convert.ToDouble(this.txtProductHeight.Text);
var size = length * width * height;
//長 x 寬 x 高(公分)x 0.0000353
if (length > 100 || width > 100 || height > 100)
{
this.lblCharge.Text = (size * 0.0000353 * 1100 + 500).ToString();
}
else
{
this.lblCharge.Text = (size * 0.0000353 * 1200).ToString();
}
}
else if (this.drpCompany.SelectedValue == "3")
{
this.lblCompany.Text = "郵局";
var weight = Convert.ToDouble(this.txtProductWeight.Text);
var feeByWeight = 80 + weight * 10;
var length = Convert.ToDouble(this.txtProductLength.Text);
var width = Convert.ToDouble(this.txtProductWidth.Text);
var height = Convert.ToDouble(this.txtProductHeight.Text);
var size = length * width * height;
var feeBySize = size * 0.0000353 * 1100;
if (feeByWeight < feeBySize)
{
this.lblCharge.Text = feeByWeight.ToString();
}
else
{
this.lblCharge.Text = feeBySize.ToString();
}
}
else
{
var js = "alert('發生不預期錯誤,請洽系統管理者');location.href='http://tw.yahoo.com/';";
this.ClientScript.RegisterStartupScript(this.GetType(), "back", js, true);
}
}
}
上面就是一陀攤在角落的code,一眼望過去,每個字都認識,但卻要動腦袋猜測,甚至動手測試才能了解這一段code是什麼意思。除了難以理解以外,這樣巢狀if的設計方式,健壯性(robustness)上也相當薄弱。
呈現的畫面與功能,如下圖所示:
@小結
要重構之前,得先瞭解重構的目的、意義,以及如何找到需要重構的程式。
期望重構之後,能對原本可以正常執行的結果完全沒有影響,但程式碼因此具備了更高的可讀性、擴充性、健壯性等等...
重構的基本原則是:
由於重構在TDD中,也佔了很重要的一個角色,所以希望接下來幾篇,可以幫助讀者手把手的跟著練習一遍,這樣看似簡單、又像複雜、又沒啥彈性的程式碼,如何從亂七八糟,變成最後一應俱全的健壯程式。
@補充
有讀者朋友問到,什麼樣的程式碼算的上是Bad smell?
這邊列出筆者工作環境中的門檻值:
以上,不代表超過標準就一定不好,但就像健康檢查報告的指數一樣,這些的確是需要被highlight出來說明的。
其他靜態程式碼分析的工具與投影片簡介,請參考這篇文章:[.NET][Tool]靜態程式碼分析工具簡介
第一次認識H大大就是看到那亂亂的房間那張圖~~ 真懷念...
XD 那我知道我們什麼時候認識的了。
請原諒我,因為這邊的發文介面比較無法像blog般自由跟順手,所以我覺得用投影片的方式來說明,應該可以讓讀者閱讀起來比較有趣跟舒服
樓主你太客氣了啦~~
你的文章的真很讚~